const sRoot = __dirname;
const osFs = require('fs');
const osPath = require('path');

const oEJS = require('ejs');

const {
  // SourceCode: oSourceCode,
  // Linter: oLinter,
  ESLint: oEslint,
} = require('eslint');

const { getExt, fsExistsSync, fnWriteFile } = require('../utils/fs');
const oEslintrc = require('./eslintrc.js');

const COMPLEXITY = /complexity of (\d*)./;
class EslintChecker {
  constructor() {
    // 检测结果集
    this.aCheckedLst = [];
    this.newEslint = undefined;
  }

  async fnCheck({ sRootPath, aFileList }) {
    // console.log(">>> Eslint扫描");
    const oDefVal = {
      cwd: osPath.join(sRootPath),
      baseConfig: {
        parserOptions: oEslintrc.parserOptions,
        env: oEslintrc.env,
        rules: oEslintrc.rules,
      },
      overrideConfig: {
        parser: oEslintrc.parser,
        plugins: oEslintrc.plugins,
      },
      useEslintrc: false,
    }
    const oGlobalVar = this.fnGetGlobalVar(sRootPath);
    oDefVal.overrideConfig.globals = oGlobalVar;
    oDefVal.overrideConfig.ignorePatterns = await this.fnGetIgnorePatterns(sRootPath);
    this.newEslint = new oEslint(oDefVal);

    return this.fnCheckLst(aFileList);
  }

  // 异步遍历
  async fnCheckLst(aFileList) {
    return Promise.all(
      aFileList.map(async (sItem) => {
        try {
          await this.fnGetEslintResultFromFilepath(sItem);
        } catch (e) {
          throw new Error(e);
        }
      })
    );
  }

  // 只处理js的eslientrc文件
  fnGetGlobalVar(sRootPath) {
    // 读取.eslintrc.js
    const sLocalEslintrcFile = osPath.join(sRootPath, '../', '.eslintrc.js');

    // 判断文件是否存在
    if(fsExistsSync(sLocalEslintrcFile)) {
      const oLocalEslintrc = require(sLocalEslintrcFile);
      return oLocalEslintrc['globals'] ? oLocalEslintrc['globals'] : {};
    }
    return {};
  }

  // 过滤忽略规则
  async fnGetIgnorePatterns(sRootPath) {
    // 读取.eslintignore
    const sEslintignoreFile = osPath.join(sRootPath, '../', '.eslintignore');
    // 判断文件是否存在
    if(fsExistsSync(sEslintignoreFile)) {
      let sLocalEslintrc = await osFs.readFileSync(sEslintignoreFile).toString();
      sLocalEslintrc = sLocalEslintrc.replace('/src', '').replace('src', '');
      return sLocalEslintrc.split("\n");
    }
    return [];
  }

  // ast树检测及加入纬度值
  fnGetEslintResultFromFilepath(sFilePath) {
    return new Promise((resolve, reject) => {
      const sExt = getExt(sFilePath);
      if (!['js', 'jsx', 'vue'].includes(sExt)) {
        return resolve(sExt);
      }

      // 读取代码源文件内容
      const oFileContent = osFs.readFileSync(sFilePath, 'utf-8');

      // 对给出的文本进行检测
      this.newEslint
        .lintText(oFileContent, {
          filePath: sFilePath,
        })
        .then((oRes) => {
          // console.log('>>>: fnGetEslintResultFromFilepath -> oRes', oRes)
          const iMaxComplexity = this.fnAddParam(oRes[0]);
          this.aCheckedLst.push({
            ...oRes[0],
            complexity: iMaxComplexity,
          });
          resolve(true);
        })
        .catch((err) => {
          console.log("err", JSON.stringify(err, null, 2));
          reject(err);
        });
    });
  }

  fnAddParam(oRes) {
    let iMaxComplexity = 0;
    (oRes && oRes['messages'] || []).map((oItem) => {
      // 复杂纬度捕捉
      if (oItem.ruleId === 'complexity') {
        const iComplexity = this.fnGetComplexityFromMessage(oItem);
        iMaxComplexity = Math.max(iMaxComplexity, iComplexity);
      }
    });
    return iMaxComplexity;
  }

  fnReport({ sReportPath, sFormat, sRootPath }) {
    if (sFormat === 'html') {
      this.fnOutputHtml(sReportPath, sRootPath);
    } else {
      const sEslintPath = osPath.join(sReportPath, 'eslint-report.json');
      fnWriteFile(
        sEslintPath,
        JSON.stringify({ eslint: this.aCheckedLst }, null, 2)
      );
    }

    return 'success';
  }

  async fnOutputHtml(sReportPath, sRootPath) {
    const sTplPath = osPath.join(sRoot, '../html-tpl/eslint.html');
    const sOutPutPath = osPath.join(sReportPath, 'eslint.html');

    const oStatistics = this.aCheckedLst.reduce(
      (oPrev, sItem) => {
        // 所有数据合计
        Object.keys(oPrev).forEach((sKey) => {
          oPrev[sKey] += sItem[sKey] || 0;
        });
        return oPrev;
      },
      {
        errorCount: 0,
        warningCount: 0,
        complexity: 0,
      }
    );

    await oEJS.renderFile(
      sTplPath,
      {
        aCheckedLst: this.aCheckedLst,
        sRootPath: sRootPath,

        oStatistics: oStatistics,
        sCheckedLstLen: this.aCheckedLst.length,
      },
      function (oErr, sData) {
        if (oErr) {
          throw new Error(oErr);
        }
        fnWriteFile(sOutPutPath, sData);
      }
    );

    return true;
  }

  /**
   * 复杂纬度数据结果捕获
   *
   * @param {*} message 检测操作的结果对象
   * @returns
   */
  fnGetComplexityFromMessage({ message }) {
    const oRes = COMPLEXITY.exec(message);
    return oRes && typeof oRes[1] === 'string' ? Number(oRes[1]) : 0;
  }

  /**
   * 返回使用的解析器
   *
   * @param {*} sFilePath
   * @returns
   */
  fnGetParser(sFilePath) {
    return getExt(sFilePath) === 'vue' ? oVueEslintParser : oBabelEslintParser;
  }
}

module.exports = EslintChecker;
